package connect

import (
	"net"
	"time"
	log "github.com/golang/glog"
	"gim/comm"
)

const CLIENT_TIMEOUT = (60 * 6)

type Client struct {
	//客户端协议版本号
	version int
	//创建时间
	tm     time.Time
	//发消息至客户端chan
	wt     chan *comm.Message

	appid  int64
	uid    int64
	device_id string
	platform_id int8
	conn   net.Conn
}

func NewClient(conn net.Conn) *Client {
	client := new(Client)
	client.conn = conn
	client.wt = make(chan *comm.Message, 10)

	return client
}

func (client *Client) Read() {
	for {
		msg := client.receive()
		if msg == nil {
			client.HandleRemoveClient()
			break
		}
		client.HandleMessage(msg)
	}
}

func (client *Client) HandleRemoveClient() {
	client.wt <- nil
	
	//清理客户端注册信息
	client.RemoveClient()
}

func (client *Client) HandleMessage(msg *comm.Message) {
	log.Info("msg cmd:", comm.Command(msg.Cmd))
	switch msg.Cmd {
	case comm.MSG_AUTH_TOKEN://通过token登录
		client.HandleAuthToken(msg.Body.(*comm.AuthenticationToken), msg.Version)
	case comm.MSG_IM, comm.MSG_GROUP_IM, comm.MSG_ROOM_IM://聊天发消息
		client.HandleIMMessage(msg.Body.(*comm.IMMessage), msg.Seq, msg.Version, msg.Cmd)
	case comm.MSG_PEER_ACK://客户端告诉服务器消息已经送达
		client.HandlePeerACK(msg.Body.(*comm.MessagePeerACK))
	case comm.MSG_HEARTBEAT:
		// nothing to do
	case comm.MSG_PING:
		client.HandlePing()
	case comm.MSG_INPUTING://输入状态
		client.HandleInputing(msg.Body.(*comm.MessageInputing))
	case comm.MSG_FRIEND_ADD, comm.MSG_FRIEND_DEL:
		client.HandleFriendMessage(msg)
	case comm.MSG_THROUGH_IN:
		client.HandleThroughMessage(msg)
	case comm.MSG_PEER_ACK_CMD:
		client.HandlePeerACKCmd(msg.Body.(*comm.MessagePeerACK))
	case comm.MSG_FRIEND_GETLIST:
		client.HandleFriendList()
	default:
		log.Info("unknown msg:", msg.Cmd)
	}
}

//发送离线消息
func (client *Client) SendOfflineMessage() {
	storage, err := storage_pool.Get()
	if err != nil {
		log.Error("connect storage err:", err)
		return
	}
	defer storage_pool.Release(storage)

	appUserId := comm.AppUserID{
		Uid : client.uid,
	}
	
	offline_messages := make([]comm.Message, 0, 5)
	err = storage.Conn.Call("StorageServer.GetMessages", &appUserId, &offline_messages)
	if err != nil {
		log.Error("load offline message err:", err)
	}

	log.Info("load offline message count:", len(offline_messages))
	for _, msg := range offline_messages {
		m := new(comm.Message)
		m.Cmd = msg.Cmd
		m.Seq = msg.Seq
		m.Version = msg.Version
		
		switch m.Cmd {
		case comm.MSG_IM, comm.MSG_GROUP_IM:
			body := msg.Body.(comm.IMMessage)
			m.Body = &comm.IMMessage {
				Sender : body.Sender,
				Receiver : body.Receiver,
				Gid : body.Gid,
				Timestamp : body.Timestamp,
				Msgid  : body.Msgid,
				Content  : body.Content,
				Extra  : body.Extra,
			}
		}
		
		client.wt <- m
	}
}

func (client *Client) SendMessage(m *comm.Message) bool {
	return rmq.Enqueue("GIM_MS_Q", m)
}

func (client *Client) AuthToken(appid int64, uid int64, token string) bool {
	t, err := manager.GetUserToken(appid, uid)
	if err != nil {
		log.Error("auth token err", err)
		return false
	}
	
	if token != t {
		return false
	}
	
	return true
}

//认证token
func (client *Client) HandleAuthToken(login *comm.AuthenticationToken, version int) {
	var m *comm.Message
	if !client.AuthToken(comm.APP_ID, login.Uid, login.Token) {
		m = &comm.Message{
			Cmd: comm.MSG_AUTH_STATUS, 
			Body: &comm.AuthenticationStatus{1},
		}
		client.wt <- m
		return
	}

	client.appid = comm.APP_ID
	client.uid = login.Uid
	client.version = version
	client.device_id = login.Device_id
	client.platform_id = login.Platform_id
	client.tm = time.Now()
	log.Infof("auth token:%s appid:%d uid:%d device id:%s", 
		login.Token, client.appid, client.uid, client.device_id)

	//注册客户端信息
	
	ret := client.AddClient()
	if !ret {
		m = &comm.Message{
			Cmd: comm.MSG_AUTH_STATUS, 
			Body: &comm.AuthenticationStatus{2},
		}
		client.wt <- m
		return
	}
	
	//判断多终端互踢
	m = &comm.Message{
		Cmd: comm.MSG_DEVICE_KICK,
		Body: &comm.DeviceKick{
			Uid : client.uid,
			Platform_id : client.platform_id,
			Device_id : client.device_id,
		},
	}
	client.SendMessage(m)
	
	//认证通过
	m= &comm.Message{
		Cmd: comm.MSG_AUTH_STATUS, 
		Body: &comm.AuthenticationStatus{0},
	}
	client.wt <- m
	
	//发送离线消息
	client.SendOfflineMessage()
}

//注册客户端信息
func (client *Client) AddClient() bool {
	ret := manager.SetUserLoginPoint(client.appid, client.uid, client.device_id, client.platform_id, csrv.host, time.Now().Unix())
	if !ret {
		return false
	} 
	
	csrv.RegisterClient(client)
	
	return true
}

//清理客户端注册信息
func (client *Client) RemoveClient() {
	manager.RemoveUserLoginPoint(client.appid, client.uid, client.device_id)
	csrv.RemoveClient(client)
}

func (client *Client) HandleFriendMessage(msg *comm.Message) {
	client.SendMessage(msg)
}

func (client *Client) HandleThroughMessage(msg *comm.Message) {
	client.SendMessage(msg)
}

//发送聊天消息
func (client *Client) HandleIMMessage(msg *comm.IMMessage, seq int64, version int, cmd int) {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}

	if msg.Sender != client.uid {
		log.Warningf("im message sender:%d client uid:%d\n", msg.Sender, client.uid)
		return
	}
	msg.Timestamp = int32(time.Now().Unix())
	m := &comm.Message{
		Cmd: cmd, 
		Version: version, 
		Body: msg,
	}

	client.SendMessage(m)

	client.wt <- &comm.Message{
		Cmd: comm.MSG_ACK, 
		Body: &comm.MessageACK{seq},
	}

	log.Infof("peer message sender:%d receiver:%d", msg.Sender, msg.Receiver)
}

//处理输入状态
func (client *Client) HandleInputing(inputing *comm.MessageInputing) {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
	
	msg := &comm.Message{
		Cmd: comm.MSG_INPUTING, 
		Body: inputing,
	}
	client.SendMessage(msg)
	log.Infof("inputting sender:%d receiver:%d", inputing.Sender, inputing.Receiver)
}

//客户端发送消息到达ack
func (client *Client) HandlePeerACK(ack *comm.MessagePeerACK) {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
	
	storage, err := storage_pool.Get()
	if err != nil {
		log.Error("connect storage err:", err)
		return
	}
	defer storage_pool.Release(storage)

	var ok bool
	err = storage.Conn.Call("StorageServer.AckMessage", ack, &ok)
	if err != nil {
		log.Error("client ack message err:", err)
	}
}

//确认cmd消息
func (client *Client) HandlePeerACKCmd(ack *comm.MessagePeerACK) {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
	
	storage, err := storage_pool.Get()
	if err != nil {
		log.Error("connect storage err:", err)
		return
	}
	defer storage_pool.Release(storage)

	var ok bool
	err = storage.Conn.Call("StorageServer.AckCmdMessage", ack, &ok)
	if err != nil {
		log.Error("client ack message err:", err)
	}
}

//ping
func (client *Client) HandlePing() {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
	
	m := &comm.Message{
		Cmd: comm.MSG_PONG,
	}
	client.wt <- m
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
}

//发送好友列表
func (client *Client) HandleFriendList() {
	if client.uid == 0 {
		log.Warning("client has't been authenticated")
		return
	}
	
	friends := manager.GetUserFriends(comm.APP_ID, client.uid)
	
	m := new(comm.Message)
	m.Cmd = comm.MSG_FRIEND_LIST
	
	friendList := new(comm.FriendList)
	friendList.Number = int32(len(friends))
	friendList.Users = make([]*comm.UserBaseInfo, friendList.Number)
	for i:=0; i<int(friendList.Number); i++ {
		u := new(comm.UserBaseInfo)
		u.Uid = friends[i].Uid
		u.Username = friends[i].Username
		u.Avatar = friends[i].Avatar
		friendList.Users[i] = u
	}
	
	m.Body = friendList
	client.wt <- m
}

func (client *Client) Write() {
	running := true
	for running {
		select {
		case msg := <-client.wt:
			if msg == nil {
				client.close()
				running = false
				log.Infof("client:%d socket closed", client.uid)
				break
			}
			client.send(msg)
		}
	}
}

// 根据连接类型获取消息
func (client *Client) receive() *comm.Message {
	client.conn.SetDeadline(time.Now().Add(CLIENT_TIMEOUT * time.Second))
	return comm.ReceiveMessage(client.conn)
}

// 根据连接类型发送消息
func (client *Client) send(msg *comm.Message) {
	comm.SendMessage(client.conn, msg)
}

// 根据连接类型关闭
func (client *Client) close() {
	client.conn.Close()
}

func (client *Client) Run() {
	go client.Write()
	go client.Read()
}